using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using Gko.Utils.Interfaces;

namespace Gko.Utils
{
	internal class BackgroundTransferObservable<T> : DisposableBase, IBackgroundTransferObservable
	{
		private readonly AutoResetEvent abortEvent = new AutoResetEvent(false);
		private readonly SynchronizationContext context;
		private readonly IEnumerable<T> source;
		private readonly IList<T> target;
		private readonly Thread thread;
		private bool isTransferring;
		private int transfered;
		private readonly ReaderWriterLockSlim transferredLock = new ReaderWriterLockSlim();
		private readonly ReaderWriterLockSlim isTransferringLock = new ReaderWriterLockSlim();

		internal BackgroundTransferObservable(SynchronizationContext context, IEnumerable<T> source, IList<T> target)
		{
			this.context = context;
			this.source = source;
			this.target = target;
			this.thread = new Thread(this.Transfer);

			this.AddDisposed(this.abortEvent);
			this.AddDisposed(this.transferredLock);
			this.AddDisposed(this.transferredLock);
		}

		#region IBackgroundTransferObservable Members

		public void Start()
		{
			this.thread.Start();
		}

		public void Abort()
		{
			this.abortEvent.Set();
		}

		public int Transferred
		{
			get
			{
				using (this.transferredLock.Read()) {
					return this.transfered;
				}
			}   
			private set
			{
				using (this.transferredLock.Write()) {
					this.transfered = value;
				}
				this.context.BeginInvoke(() => this.PropertyChanged.Raise(this, "Transferred"));
			}
		}

		public bool IsTransferring
		{
			get
			{
				using (this.isTransferringLock.Read()) {
					return this.isTransferring;
				}
			}
			private set
			{
				using (this.isTransferringLock.Write()) {
					this.isTransferring = value;
				}
				this.context.BeginInvoke(() => this.PropertyChanged.Raise(this, "IsTransferring"));
			}
		}

		public event EventHandler Complete;
		public event EventHandler Aborted;

		#endregion

		#region INotifyPropertyChanged Members

		public event PropertyChangedEventHandler PropertyChanged;

		#endregion

		private void Transfer()
		{
			Interlocked.Exchange(ref this.transfered, 0);
			this.IsTransferring = true;
			foreach (var item in this.source) {
				if (this.abortEvent.WaitOne(TimeSpan.Zero)) {
					this.OnFinished(this.Aborted);
					return;
				}
				this.context.Invoke(this.target.Add, item);
				this.Transferred++;
			}
			this.OnFinished(this.Complete);
		}

		private void OnFinished(EventHandler handler)
		{
			this.IsTransferring = false;
			this.context.BeginInvoke(() => handler.Raise(this));
		}
	}
}